Skip to main content

You Integrate us

InterSwap creates an easy permissionless integration level to enable cross-chain liquidity flow for existing applications. So any existing multichain AMM or DEX, Aggregator can become a cross-chain application, just by deploying one smart contract. Cross-chain Lending protocol can integrate InterSwap for enabling cross-chain features.

We are providing “out-of-the-box” examples for Uniswap and 1inch Liquidity protocols.

UniswapV2CrossRouter.sol example

  pragma solidity ^0.8.11;
import "../../interfaces/IInterswapCallee.sol";
import "../../interfaces/IInterswapCommunicator.sol";
import "../../interfaces/IERC20.sol";
import "./interfaces/IUniswapV2Router.sol";
import "../../libraries/TransferHelper.sol";

contract UniswapV2CrossRouter is IInterswapCallee, IMessageObj, IGMPGasObj { address public immutable interswapCommunicator; address public immutable uniswapV2Router;
struct PostSwap {
bytes[] path;
bytes receiver;
uint256 amountOutMin;
}

struct PreSwap {
uint256 amountIn;
address[] path;
uint256 deadline;
}

constructor(address _interswapCommunicator, address _uniswapV2Router) {
interswapCommunicator = _interswapCommunicator;
uniswapV2Router = _uniswapV2Router;
}

function interswapCall(
address token,
uint256 amount,
bytes calldata data
) external {
PostSwap memory postSwap = abi.decode(data, (PostSwap));
TransferHelper.safeApprove(token, uniswapV2Router, amount);

// convert bytes to native address format
address[] memory path = new address[](postSwap.path.length);
address receiver = bytesToAddress(postSwap.receiver);
for (uint256 i; i < postSwap.path.length; ++i) {
path[i] = bytesToAddress(postSwap.path[i]);
}

// do post swap and send final token to receiver
try IUniswapV2Router(uniswapV2Router).swapExactTokensForTokens(
amount,
postSwap.amountOutMin, // here we are checking amountOutMin finally, if it fails - we will send "token" as result
path,
receiver,
block.timestamp * 2 // deadline is
) {
// do nothing
} catch {
// sending "token" as a result
TransferHelper.safeTransfer(token, receiver, amount);
}
}

function swapExactTokensForTokens(
messageObj memory _messageObj,
GMPGasObj memory _GMPGasObj,
PreSwap calldata preSwap,
PostSwap calldata postSwap
) external payable {
address srcCrossTokenAddress = bytesToAddress(_messageObj.tokenA);
if (preSwap.path.length != 0) {
require(preSwap.path[preSwap.path.length - 1] == srcCrossTokenAddress, "invalid preSwap src token address");
TransferHelper.safeTransferFrom(
preSwap.path[0],
msg.sender,
address(this),
preSwap.amountIn
);
TransferHelper.safeApprove(preSwap.path[0], uniswapV2Router, preSwap.amountIn);

uint256 srcCrossAmountBefore = IERC20(srcCrossTokenAddress).balanceOf(
address(this)
);
IUniswapV2Router(uniswapV2Router).swapExactTokensForTokens(
preSwap.amountIn,
0,
preSwap.path,
address(this),
preSwap.deadline
);
_messageObj.amountA =
IERC20(srcCrossTokenAddress).balanceOf(address(this)) -
srcCrossAmountBefore; // change amountIn after preSwap
TransferHelper.safeApprove(
srcCrossTokenAddress,
interswapCommunicator,
_messageObj.amountA
);
}

if (postSwap.path.length != 0) {
// need to do additional post swap using uniswapV2Router on destination chain
require(
keccak256(_messageObj.tokenB) == keccak256(postSwap.path[0]) || postSwap.path.length == 1,
"invalid postSwap path"
);
_messageObj.data = abi.encode(postSwap);
_messageObj.amountB = 0;

IInterswapCommunicator(interswapCommunicator).swap(_messageObj, _GMPGasObj);
} else {
// no need to do any post swap
_messageObj.data = bytes("");
IInterswapCommunicator(interswapCommunicator).swap(_messageObj, _GMPGasObj);
}
}

function bytesToAddress(bytes memory bys)
internal
pure
returns (address addr)
{
assembly {
addr := mload(add(bys, 20))
}
}